home *** CD-ROM | disk | FTP | other *** search
/ Dr. Windows 3 / dr win3.zip / dr win3 / WINPROGS / DRDOBBS.ZIP / TOPICDMP.C < prev    next >
C/C++ Source or Header  |  1993-08-04  |  14KB  |  374 lines

  1. /* TOPICDMP.C -- Dumps topic file from a Windows .HLP or .MVB file.
  2. Pete Davis, August 1993
  3. With some modifications by Andrew Schulman, September 1993
  4. From Dr. Dobb's Journal, October 1993 */
  5.  
  6. #include <time.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <conio.h>
  11. #include <ctype.h>
  12. #include <limits.h>
  13.  
  14. #pragma pack(1)   /* Make sure we get byte alignment */
  15. #include "whstruct.h"
  16. #include "topicdmp.h"
  17.  
  18. HELPHEADER        HelpHeader;        /* Header for Help file.       */
  19. WHIFSBTREEHEADER  WHIFSHeader;       /* WHIFS Header record         */
  20. int               WHIFSLeafOne = -1; /* First WHIFS Leaf Node       */
  21. long              FirstPageLoc;      /* Used by macros for b-trees  */
  22. char              *PhrasesPtr; 
  23. int               Compressed;        /* Is there compression?       */
  24.  
  25. #define MSG(s)              { puts(s); return; }
  26. #define FAIL(s)             { puts(s); exit(1); }
  27.  
  28. #define GET_STRING(f, s) \
  29.     { char *p = (char *)(s); while (*p++ = fgetc(f)) ; *p = 0; }
  30.  
  31. #define BIT_SET(map, bit)   (((map) & (1 << (bit))) ? 1 : 0)
  32.  
  33. // Finds the first leaf in the WHIFS B-Tree
  34. void WHIFSGetFirstLeaf(FILE *HelpFile) {
  35.     int               CurrLevel = 1; /* Current Level in B-Tree */
  36.     BTREEINDEXHEADER  CurrNode;      /* Current Node in B-Tree  */
  37.     int               NextPage = 0;  /* Next Page to go to      */
  38.  
  39.     /* Go to the beginning of WHIFS B-Tree */
  40.     fseek(HelpFile, HelpHeader.WHIFS, SEEK_SET);
  41.     fread(&WHIFSHeader, sizeof(WHIFSHeader), 1, HelpFile);
  42.     FirstPageLoc = HelpHeader.WHIFS + sizeof(WHIFSHeader);
  43.     GotoWHIFSPage(WHIFSHeader.RootPage);  // macro in WHSTRUCT.H
  44.  
  45.     /* Find First Leaf */
  46.     while (CurrLevel < WHIFSHeader.NLevels) {
  47.        fread(&CurrNode, sizeof(CurrNode), 1, HelpFile);
  48.  
  49.        /* Next Page is conveniently the first byte of the page */
  50.        fread(&NextPage, sizeof(int), 1, HelpFile);
  51.        GotoWHIFSPage(NextPage);
  52.        CurrLevel++;
  53.     }
  54.     /* First Leaf page is here */
  55.     WHIFSLeafOne = NextPage;
  56. }
  57.  
  58. // Get a WHIFS file by file number; returns offset and filename
  59. void GetFile(FILE *HelpFile, DWORD Number, long *Offset, char *Name) {
  60.     BTREENODEHEADER CurrentNode;      
  61.     DWORD           CurrPage, counter = 0;
  62.     char            c, TempFile[19];
  63.     
  64.     /* Skip pages we don't need */
  65.     CurrentNode.NextPage = WHIFSLeafOne;
  66.     do {
  67.         CurrPage = CurrentNode.NextPage;
  68.         GotoWHIFSPage(CurrPage);
  69.         fread(&CurrentNode, sizeof(CurrentNode), 1, HelpFile);
  70.         counter += CurrentNode.NEntries;
  71.     } while (counter < Number);
  72.  
  73.     for (counter -= CurrentNode.NEntries; counter <= Number; counter++) {
  74.         GET_STRING(HelpFile, TempFile);
  75.         fread(Offset, sizeof(long), 1, HelpFile);
  76.     }
  77.     strcpy(Name, TempFile);
  78. }
  79.  
  80. // Get SysHeader to see if compression used on help file
  81. void SysLoad(FILE *HelpFile, long FileStart) {
  82.    SYSTEMHEADER    SysHeader;
  83.    FILEHEADER      FileHdr;
  84.    fseek(HelpFile, FileStart, SEEK_SET);
  85.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  86.    fread(&SysHeader, sizeof(SysHeader), 1, HelpFile);
  87.    if (SysHeader.Revision != 21)
  88.        FAIL("Sorry, TOPICDMP only works with Windows 3.1 help files");
  89.    Compressed = (SysHeader.Flags & COMPRESSION_310) ||
  90.                 (SysHeader.Flags & COMPRESSION_UNKN);
  91. }
  92.  
  93. // Decides how many bytes to read, depending on number of bits set
  94. int BytesToRead(BYTE BitMap) {
  95.     int TempSum, counter;
  96.     TempSum = 8;
  97.     for (counter = 0; counter < 8; counter ++)
  98.        TempSum += BIT_SET(BitMap, counter);
  99.     return TempSum;
  100. }
  101.  
  102. // Decompresses the data using Microsoft's LZ77 derivative.
  103. long Decompress(FILE *HelpFile, long CompSize, char *Buffer) {
  104.    long InBytes = 0;        /* How many bytes read in                    */
  105.    WORD OutBytes = 0;       /* How many bytes written out                */
  106.    BYTE BitMap, Set[16];    /* Bitmap and bytes associated with it       */
  107.    long NumToRead;          /* Number of bytes to read for next group    */
  108.    int  counter, Index;     /* Going through next 8-16 codes or chars    */
  109.    int  Length, Distance;   /* Code length and distance back in 'window' */
  110.    char *CurrPos;           /* Where we are at any given moment          */
  111.    char *CodePtr;           /* Pointer to back-up in LZ77 'window'       */
  112.  
  113.    CurrPos = Buffer;
  114.    while (InBytes < CompSize) {
  115.       BitMap = (BYTE) fgetc(HelpFile);
  116.       NumToRead = BytesToRead(BitMap);
  117.  
  118.       if ((CompSize - InBytes) < NumToRead) 
  119.           NumToRead = CompSize - InBytes;   // only read what we have left
  120.       fread(Set, 1, (int) NumToRead, HelpFile);    
  121.       InBytes += NumToRead + 1;
  122.  
  123.       /* Go through and decode data */
  124.       for (counter = 0, Index = 0; counter < 8; counter++) {
  125.          /* It's a code, so decode it and copy the data */
  126.          if (BIT_SET(BitMap, counter)) {
  127.             Length = ((Set[Index+1] & 0xF0) >> 4) + 3;
  128.             Distance = (256 * (Set[Index+1] & 0x0F)) + Set[Index] + 1;
  129.             CodePtr = CurrPos - Distance;   // ptr into decompress window
  130.             while (Length)
  131.                { *CurrPos++ = *CodePtr++; OutBytes++; Length--; } 
  132.             Index += 2;  /* codes are 2 bytes */
  133.          }
  134.          else 
  135.             { *CurrPos++ = Set[Index++]; OutBytes++; }
  136.       }
  137.    }
  138.    return OutBytes;
  139.    
  140. // Prints a Phrase from the Phrase table
  141. void PrintPhrase(char *Phrases, int PhraseNum) {
  142.     int *Offsets = (int *)Phrases;
  143.     char *p = Phrases+Offsets[PhraseNum];
  144.     while (p < Phrases + Offsets[PhraseNum + 1])
  145.         { putchar(*p); p++; }
  146. }
  147.  
  148. // Build up a table of phrases
  149. void PhrasesLoad(FILE *HelpFile, long FileStart) {
  150.    FILEHEADER      FileHdr;
  151.    PHRASEHDR       PhraseHdr;
  152.    int             *Offsets;
  153.    char            *Phrases;
  154.    long            DeCompSize;
  155.  
  156.    /* Go to the phrases file and get the headers */
  157.    fseek(HelpFile, FileStart, SEEK_SET);
  158.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  159.    fread(&PhraseHdr, sizeof(PhraseHdr), 1, HelpFile);
  160.  
  161.    /* Allocate space and decompress if it's compressed, else read in. */
  162.    if (Compressed) {
  163.       if ((Offsets = malloc((unsigned) (PhraseHdr.PhrasesSize + 
  164.           (PhraseHdr.NumPhrases + 1) * 2))) == NULL)
  165.         MSG("No room to decompress |Phrases");
  166.       Phrases = Offsets + fread(Offsets,2,PhraseHdr.NumPhrases+1, HelpFile);
  167.       DeCompSize = Decompress(HelpFile, (long)FileHdr.FileSize - 
  168.           (sizeof(PhraseHdr) + 2 * (PhraseHdr.NumPhrases+1)), Phrases);
  169.       if (DeCompSize != PhraseHdr.PhrasesSize) {
  170.          printf("\n");
  171.       }
  172.    }
  173.    else {
  174.       if (!(Offsets=malloc((unsigned)(FileHdr.FileSize-sizeof(PhraseHdr)))))
  175.          MSG("No room to decompress |Phrases");
  176.       /* Backup 4 bytes for uncompressed Phrases (no PhrasesSize) */
  177.       fseek(HelpFile, -4, SEEK_CUR);
  178.       fread(Offsets, (unsigned) (FileHdr.FileSize - 4), 1, HelpFile);
  179.    }
  180.    PhrasesPtr = Phrases = (char *) Offsets;
  181. }
  182.  
  183. /* Because the topic file is broken into 4k blocks, we'll have to handle
  184. all the reads.  The idea is to filter out the TOPICBLOCKHEADERs and
  185. do any decompression that needs doing. */
  186. long TopicRead(BYTE *Dest, long NumBytes, FILE *HelpFile) {
  187.    static long        CurrBlockLoc = 0;   /* Where we are in the block  */
  188.    static BYTE        *DCmpBlock = NULL;  /* Block of uncompressed data */
  189.    static long        DecompSize;         /* Size of block after decomp */
  190.    static long        TopicStart, BlkNum; /* Start of |TOPIC file       */
  191.    long               BytesLeft;          /* # Bytes left to return     */
  192.    TOPICBLOCKHEADER   BlockHeader;
  193.    TOPICLINK          *TempLink;
  194.    long               EndOffset;
  195.  
  196.    /* If NumBytes = 0, then we're done and need to free memory */
  197.    if (NumBytes == -1) { free(DCmpBlock); return 0; }
  198.  
  199.    if (!DCmpBlock) {
  200.       if (Compressed) {
  201.          if (! (DCmpBlock = malloc((unsigned) (4 * TopicBlockSize))))
  202.              FAIL("Not enough memory to decompress |TOPIC file");
  203.          TopicStart = ftell(HelpFile);
  204.          BlkNum = 0;
  205.       }
  206.       else if (! (DCmpBl